#include "AudioDecodeRaw.h"
#include "FmtCvt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define AUDIO_REFILL_THRESH 4096

AudioDecodeRaw::AudioDecodeRaw():
    m_codec(NULL),
    m_codec_ctx(NULL),
    m_parser_ctx(NULL),
    m_pkt(NULL),
    m_frm(NULL),
    m_swr(NULL),
    m_buffer(NULL),
    m_buffer_size(0),
    m_buffer_capacity(32000)
{
    m_swr = new FmtCvt();
    m_buffer = (uint8_t*)malloc(m_buffer_capacity);
    //m_backup = (uint8_t*)malloc(m_backup_capacity);

    av_register_all();
    avcodec_register_all();

    /*
    AVCodec *codec = NULL;
    while ((codec = av_codec_next(codec)))
    {
        if(av_codec_is_decoder(codec))
        {
            if(codec->type == AVMEDIA_TYPE_AUDIO)
            {
                printf("decoder: %s\n", codec->name);
            }
        }
    }
    printf("decoder: over\n");
    */
}

AudioDecodeRaw::~AudioDecodeRaw()
{
    destroy();
    delete m_swr;
    if(NULL!=m_buffer)
    {
        free(m_buffer);
        m_buffer = NULL;
    }
    //if(NULL!=m_backup)
    //{
        //free(m_backup);
        //m_backup = NULL;
    //}
}

int AudioDecodeRaw::init(const std::string& id)
{
    if(m_codec)
    {
        return -1;
    }
    /* 查找解码器 */
    m_codec = avcodec_find_decoder_by_name(id.c_str());
    if (!m_codec) {
        fprintf(stderr, "codec %s not found\n", id.c_str());
        //exit(1);
        return -2;
    }

    return _init();
}

int AudioDecodeRaw::init(enum AVCodecID id)
{
    if(m_codec)
    {
        return -1;
    }
    /* 查找解码器 */
    m_codec = avcodec_find_decoder(id);
    if (!m_codec) {
        fprintf(stderr, "Codec not found, codec_id = 0x%x \n", id);
        //exit(1);
        return -2;
    }

    return _init();
}

int AudioDecodeRaw::_init()
{
    //if(m_codec)
    //{
        //return -1;
    //}
    /* 查找mp3解码器 */
    //m_codec = avcodec_find_decoder(id);
    //if (!m_codec) {
        //fprintf(stderr, "Codec not found\n");
        ////exit(1);
        //return -2;
    //}

    m_codec_ctx = avcodec_alloc_context3(m_codec);
    if (!m_codec_ctx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        destroy();
        return -3;
    }

    /* 打开解码器*/
    if (avcodec_open2(m_codec_ctx, m_codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec, codec_id = 0x%x \n", m_codec->id);
        destroy();
        return -4;
    }

    m_parser_ctx = av_parser_init(m_codec->id);
    if (!m_parser_ctx) {
        fprintf(stderr, "Parser not found, codec_id = 0x%x \n", m_codec->id);
        destroy();
        return -5;
    }

    if (!(m_pkt = av_packet_alloc())) {
        fprintf(stderr, "Could not allocate audio packet\n");
        destroy();
        return -6;
    }

    if (!(m_frm = av_frame_alloc())) {
        fprintf(stderr, "Could not allocate audio frame\n");
        destroy();
        return -7;
    }

    return 0;
}

int AudioDecodeRaw::destroy()
{
    if(m_codec)
    {
        m_codec = NULL;
    }
    if(m_codec_ctx)
    {
        avcodec_free_context(&m_codec_ctx);
        m_codec_ctx = NULL;
    }
    if(m_parser_ctx)
    {
        av_parser_close(m_parser_ctx);
        m_parser_ctx = NULL;
    }
    if(m_frm)
    {
        av_frame_free(&m_frm);
        m_frm = NULL;
    }
    if(m_pkt)
    {
        av_packet_free(&m_pkt);
        m_pkt = NULL;
    }
    return 0;
}

int AudioDecodeRaw::decode(const uint8_t* data, int data_size, uint8_t** out_pptr)
{
    if(!m_codec)
    {
        return -1;
    }

    //if((m_backup_size+data_in_size) > m_backup_capacity)
    //{
        //m_backup_capacity = m_backup_size+data_in_size;
        //m_backup = (uint8_t*)realloc(m_backup, m_backup_capacity);
    //}
    //memmove(m_backup+m_backup_size, data_in, data_in_size);
    //m_backup_size += data_in_size;

    //uint8_t* data = m_backup;
    //int data_size = m_backup_size;
    //int old_size = m_backup_size;
    //if (data_size < AUDIO_REFILL_THRESH) {
        ////exit(1);
    //}

    m_buffer_size = 0;
    *out_pptr = m_buffer;

    while (data_size > 0) {
        //printf("in size = %d\n", data_size);
        int ret = 0;
        if(!m_pkt->data)
        {
            m_pkt->data = (uint8_t*)malloc(data_size);
        }
        if(m_parser_ctx)
        {
            ret = av_parser_parse2(m_parser_ctx, m_codec_ctx, &m_pkt->data, &m_pkt->size,
                    data, data_size,
                    AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
        }else{
                fprintf(stderr, "Error parser is null\n");
                exit(1);
        }
        if (m_pkt->size)
        {
            _decode(m_codec_ctx, m_pkt, m_frm);
        }
        data      += ret;
        data_size -= ret;
        //printf("out size = %d\n", ret);
    }

    return m_buffer_size;
}

//FILE *outfile
bool AudioDecodeRaw::_decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame)
{
    /* 发送编码数据包给解码器 */
    int ret = avcodec_send_packet(dec_ctx, pkt);
    //if (ret < 0) {
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){
        fprintf(stderr, "Error submitting the packet to the decoder\n");
        return false;
    }

    //printfFormat(dec_ctx);

    //static FILE* outfile = NULL;
    //if(0==outfile)
    //{
        //outfile = fopen("out.pcm", "wb");
    //}

    /////////////////////////////////////////
    /* 读取所有的输出帧*/
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return false;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            //return false;
            exit(1);
        }
        int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
        if (data_size < 0) {
            /* 不应该出现的情况 */
            fprintf(stderr, "Failed to calculate data size\n");
            exit(1);
        }
        //for (int i = 0; i < frame->nb_samples; i++)
        //for (int ch = 0; ch < dec_ctx->channels; ch++)
        //fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
        //}
        /////////////////////////////////////////

        if (frame->channels > 0 && frame->channel_layout == 0)
            frame->channel_layout = av_get_default_channel_layout(frame->channels);
        else if (frame->channels == 0 && frame->channel_layout > 0)
            frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);

        //printf("channel_num : %d\n", av_get_channel_layout_nb_channels(frame->channel_layout));
        //printf("sample_fmt : %s\n", av_get_sample_fmt_name((AVSampleFormat)frame->format));
        //printf("bytes_per_sample : %d\n", av_get_bytes_per_sample((AVSampleFormat)frame->format));
        //printf("sample_rate : %d\n", frame->sample_rate);

        uint8_t* out_buffer;
        //m_swr->init(dec_ctx);
        int size = m_swr->convert(frame, &out_buffer);
        if(size>0)
        {
            if((m_buffer_size+size) > m_buffer_capacity)
            {
                m_buffer_capacity = m_buffer_size+size;
                m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_capacity);
            }
            memcpy(m_buffer+m_buffer_size, out_buffer, size);
            m_buffer_size += size;
        }

        //static FILE* outdec = NULL;
        //if(0==outdec)
        //{
        //outdec = fopen("outdec.pcm", "wb");
        //}
        //if(0!=outdec)
        //{
        //fwrite(out_buffer, 1, size, outdec);
        //}
    }
    return true;
}


